<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<title>GTK+中的树状列表构件(GtkTreeView)</title>
<link rel="stylesheet" href="/cfg/format.css" type="text/css">
<meta http-equiv="content-type" content="text/html; charset=utf-8">
<meta name="keywords" content="GUI, GtkTreeView Widget, GTK+ library, C">
<meta nam="description" content="GtkTreeView Widget">
<meta name="language" content="zh, en">
<meta name="author" content="Jan Bodnar">
<meta name="distribution" content="global">

<script type="text/javascript" src="/lib/jquery.js"></script>
<script type="text/javascript" src="/lib/common.js"></script>

</head>

<body>

<div class="container">

<div id="wide_ad" class="ltow">
<script type="text/javascript"><!--
google_ad_client = "pub-9706709751191532";
/* 160x600, August 2011 */
google_ad_slot = "2484182563";
google_ad_width = 160;
google_ad_height = 600;
//-->
</script>
<script type="text/javascript"
src="http://pagead2.googlesyndication.com/pagead/show_ads.js">
</script>
</div>


<div class="content">

<a href="/" title="Home">Home</a>&nbsp;
<a href="..">Contents</a>


<h1>GTK+中的树状列表构件(GtkTreeView)</h1>

<p>
在本章的GTK+程序设计教程中，我们将向大家重点介绍非常常用也有点复杂的构件——GtkTreeView 。
</p>

<p>
<b class="keyword">GtkTreeView</b> 构件是一个高级的构件，利用他你就可以制作出漂亮的普通列表或者是树状的列表。这个构件里可以包含一或者多行。他的构架呢？正是采用了大名鼎鼎的MVC (Model View Controller) 设计框架。也就是说数据和显示方式是进行了一种分离的操作。 
</p>

<div class="center"> 
<script type="text/javascript"><!--
google_ad_client = "pub-9706709751191532";
/* horizontal */
google_ad_slot = "1734478269";
google_ad_width = 468;
google_ad_height = 60;
//-->
</script> 
<script type="text/javascript"
src="http://pagead2.googlesyndication.com/pagead/show_ads.js"> 
</script> 
</div>
<p>
之前我们有说过复杂这个问题，于是在GtktreeView构件中确实还有着其他几个独立的对象结构（objects）。其中 
<b class="keyword">GtkCellRenderer</b>  就决定了在<b class="keyword">GtkTreeViewColumn</b>. 
中的数据究竟是如何来进行显示呈现的。<b class="keyword">GtkListStore</b> 和 <b class="keyword">GtkTreeStore</b> 
的功能为体现模型（model）的作用。也就是说他们是用来处理和分析将要在GtkTreeView显示的数据的。  
<b class="keyword">GtkTreeIter</b> 则是一个数据结构被用于在GtkTreeView构件中，对行中的数据进行操作。 
<b class="keyword">GtkTreeSelection</b>  则是用来处理选项的。
</p>


<h2>一个简单的列表构件示例(Simple List View)</h2>

<p>
在这个例子中将向大家展示一个简单的列表效果。显示的数据仅仅是文本。 
</p>

<pre class="code">
#include &lt;gtk/gtk.h&gt;

enum
{
  LIST_ITEM = 0,
  N_COLUMNS
};

static void
init_list(GtkWidget *list)
{

  GtkCellRenderer *renderer;
  GtkTreeViewColumn *column;
  GtkListStore *store;

  renderer = gtk_cell_renderer_text_new();
  column = gtk_tree_view_column_new_with_attributes("List Items",
          renderer, "text", LIST_ITEM, NULL);
  gtk_tree_view_append_column(GTK_TREE_VIEW(list), column);

  store = gtk_list_store_new(N_COLUMNS, G_TYPE_STRING);

  gtk_tree_view_set_model(GTK_TREE_VIEW(list), 
      GTK_TREE_MODEL(store));

  g_object_unref(store);
}

static void
add_to_list(GtkWidget *list, const gchar *str)
{
  GtkListStore *store;
  GtkTreeIter iter;

  store = GTK_LIST_STORE(gtk_tree_view_get_model
      (GTK_TREE_VIEW(list)));

  gtk_list_store_append(store, &amp;iter);
  gtk_list_store_set(store, &amp;iter, LIST_ITEM, str, -1);
}


void  on_changed(GtkWidget *widget, gpointer label) 
{
  GtkTreeIter iter;
  GtkTreeModel *model;
  char *value;


  if (gtk_tree_selection_get_selected(
      GTK_TREE_SELECTION(widget), &amp;model, &amp;iter)) {

    gtk_tree_model_get(model, &amp;iter, LIST_ITEM, &amp;value,  -1);
    gtk_label_set_text(GTK_LABEL(label), value);
    g_free(value);
  }

}

int main (int argc, char *argv[])
{

  GtkWidget *window;
  GtkWidget *list;

  GtkWidget *vbox;
  GtkWidget *label;
  GtkTreeSelection *selection; 

  gtk_init(&amp;argc, &amp;argv);


  window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
  gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
  gtk_container_set_border_width(GTK_CONTAINER(window), 10);
  gtk_widget_set_size_request(window, 270, 250);
  gtk_window_set_title(GTK_WINDOW(window), "List View");

  list = gtk_tree_view_new();
  gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(list), FALSE);

  vbox = gtk_vbox_new(FALSE, 0);

  gtk_box_pack_start(GTK_BOX(vbox), list, TRUE, TRUE, 5);

  label = gtk_label_new("");
  gtk_label_set_justify(GTK_LABEL(label), GTK_JUSTIFY_CENTER);
  gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, FALSE, 5);

  gtk_container_add(GTK_CONTAINER(window), vbox);

  init_list(list);
  add_to_list(list, "Aliens");
  add_to_list(list, "Leon");
  add_to_list(list, "Capote");
  add_to_list(list, "Saving private Ryan");
  add_to_list(list, "Der Untergang");


  selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(list));

  g_signal_connect(selection, "changed", 
      G_CALLBACK(on_changed), label);

  g_signal_connect(G_OBJECT (window), "destroy",
      G_CALLBACK(gtk_main_quit), NULL);

  gtk_widget_show_all(window);

  gtk_main ();

  return 0;
}
</pre>

<p>
在我们上面的这个示例代码中，我们将向大家展示的是5个条目并布置于<b class="keyword">GtkTreeView</b> 构件中。我们首先在window中放置一个<b class="keyword">GtkVBox</b>  构件。 在这个 GtkVBox 构件中含有两个构件：<b class="keyword">GtkTreeView</b>和<b class="keyword">GtkLabel</b>。
</p>

<pre class="explanation">
  list = gtk_tree_view_new();
  gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(list), FALSE);
</pre>

<p>
上面的代码生成了一个 <b class="keyword">GtkTreeView</b> 构件并且栏数被设置为FALSE即只有一栏。 
</p>

<pre class="explanation">
  label = gtk_label_new("");
  gtk_label_set_justify(GTK_LABEL(label), GTK_JUSTIFY_CENTER);
  gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, FALSE, 5);
</pre>

<p>
生成了一个 <b class="keyword">GtkLabel</b>构件，并且把它放置在<b class="keyword">GtkTreeView</b>构件的下方，设置为居中。 
</p>

<pre class="explanation">
 init_list(list);
</pre>

<p>
调用list()函数，初始化构件list。 
</p>

<pre class="explanation">
 renderer = gtk_cell_renderer_text_new();
 column = gtk_tree_view_column_new_with_attributes("List Items",
         renderer, "text", LIST_ITEM, NULL);
 gtk_tree_view_append_column(GTK_TREE_VIEW(list), column);
</pre>

<p>
在初始化函数中，我们生成了只有一栏的GtkTreeView。 
</p>

<pre class="explanation">
 store = gtk_list_store_new(N_COLUMNS, G_TYPE_STRING);

 gtk_tree_view_set_model(GTK_TREE_VIEW(list), 
     GTK_TREE_MODEL(store));
</pre>

<p>
接下来我们又生成了一个<b class="keyword">GtkListStore</b> 构件(a model) 然后把它与list 构件绑定。 
</p>

<pre class="explanation">
 g_object_unref(store);
</pre>

<p>
这个 model 被自动的销毁，以释放内存空间。 
</p>

<pre class="explanation">
 add_to_list(list, "Aliens");
</pre>

<p>
上面就是在调用add_to_list（）函数，实现向list 中在增加一个选项的功能。 
</p>

<pre class="explanation">
 store = GTK_LIST_STORE(gtk_tree_view_get_model
     (GTK_TREE_VIEW(list)));

 gtk_list_store_append(store, &amp;iter);
 gtk_list_store_set(store, &amp;iter, LIST_ITEM, str, -1);
</pre>

<p>
在函数<b class="keyword">add_to_list()</b> 中，我们利用系统函数<b class="keyword">gtk_tree_view_get_model()</b>来获得model。我们生成新的一行并把行中的数据交给model处理，这里正是借助 <b class="keyword">GtkTreeIter</b>来完成这个功能。 
</p>

<pre class="explanation">
 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(list));
</pre>

<p>
 <b class="keyword">GtkTreeSelection</b>际上并不需要明确生成。在这里，我们是利用 <b class="keyword">GtkTreeView</b>构件自动来生成。来帮助完成这项工作的正如你所见到的是系统函数<b class="keyword">gtk_tree_view_get_selection()</b>。 
</p>

<pre class="explanation">
 g_signal_connect(selection, "changed", 
     G_CALLBACK(on_changed), label);
</pre>

<p>
这个就很好理解了，把changed 信号与 <b class="keyword">GtkTreeSelection</b>绑定，我们就可以与回调函数 <b class="keyword">on_changed()</b>建立了联系。
</p>

<pre class="explanation">
 gtk_tree_model_get(model, &amp;iter, LIST_ITEM, &amp;value,  -1);
 gtk_label_set_text(GTK_LABEL(label), value);
</pre>

<p>
在这个回调函数里，我们取得了对应行的数据，当然是通过iter 来获取的。 
</p>

<br>
<img src="../images/listview.png" alt="List View">
<div class="figure">Figure: List View</div>
<br>


<h2>高级列表（Advanced List View）</h2>

<p>
在第二个例子中，我们将在前者的基础上填加一些额外的功能。我们将实现能够列表中填加或者去处其中的数据项。 
</p>

<pre class="code">
#include &lt;gtk/gtk.h&gt;

enum
{
  LIST_ITEM = 0,
  N_COLUMNS
};

GtkWidget *list;


static void
append_item(GtkWidget *widget, gpointer entry)
{
  GtkListStore *store;
  GtkTreeIter  iter;

  const char *str = gtk_entry_get_text(entry); 

  store = GTK_LIST_STORE(gtk_tree_view_get_model(
       GTK_TREE_VIEW(list)));

  gtk_list_store_append(store, &amp;iter);
  gtk_list_store_set(store, &amp;iter, LIST_ITEM, str, -1);
}

static void
remove_item(GtkWidget *widget, gpointer selection)
{
  GtkListStore *store;
  GtkTreeModel *model;
  GtkTreeIter  iter;


  store = GTK_LIST_STORE(gtk_tree_view_get_model(
      GTK_TREE_VIEW (list)));
  model = gtk_tree_view_get_model (GTK_TREE_VIEW (list));

  if (gtk_tree_model_get_iter_first(model, &amp;iter) == FALSE) 
      return;

  if (gtk_tree_selection_get_selected(GTK_TREE_SELECTION(selection), 
      &amp;model, &amp;iter)) {
    gtk_list_store_remove(store, &amp;iter);
  }
}

static void
remove_all(GtkWidget *widget, gpointer selection)
{
  GtkListStore *store;
  GtkTreeModel *model;
  GtkTreeIter  iter;


  store = GTK_LIST_STORE(gtk_tree_view_get_model(
      GTK_TREE_VIEW (list)));
  model = gtk_tree_view_get_model (GTK_TREE_VIEW (list));

  if (gtk_tree_model_get_iter_first(model, &amp;iter) == FALSE) 
      return;
  gtk_list_store_clear(store);
}

static void
init_list(GtkWidget *list)
{

  GtkCellRenderer    *renderer;
  GtkTreeViewColumn  *column;
  GtkListStore       *store;

  renderer = gtk_cell_renderer_text_new();
  column = gtk_tree_view_column_new_with_attributes("List Item",
          renderer, "text", LIST_ITEM, NULL);
  gtk_tree_view_append_column(GTK_TREE_VIEW (list), column);

  store = gtk_list_store_new (N_COLUMNS, G_TYPE_STRING);

  gtk_tree_view_set_model(GTK_TREE_VIEW (list), 
      GTK_TREE_MODEL(store));

  g_object_unref(store);
}


int main (int argc, char *argv[])
{

  GtkWidget *window;
  GtkWidget *sw;

  GtkWidget *remove;
  GtkWidget *add;
  GtkWidget *removeAll;
  GtkWidget *entry;

  GtkWidget *vbox;
  GtkWidget *hbox;

  GtkTreeSelection *selection; 

  gtk_init(&amp;argc, &amp;argv);


  window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
  sw = gtk_scrolled_window_new(NULL, NULL);
  list = gtk_tree_view_new();

  gtk_window_set_title (GTK_WINDOW (window), "List View");
  gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
  gtk_container_set_border_width (GTK_CONTAINER (window), 10);
  gtk_widget_set_size_request (window, 370, 270);

  gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW(sw),
            GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);

  gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW(sw),
            GTK_SHADOW_ETCHED_IN);

  gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (list), FALSE);

  vbox = gtk_vbox_new(FALSE, 0);

  gtk_box_pack_start(GTK_BOX(vbox), sw, TRUE, TRUE, 5);

  hbox = gtk_hbox_new(TRUE, 5);

  add = gtk_button_new_with_label("Add");
  remove = gtk_button_new_with_label("Remove");
  removeAll = gtk_button_new_with_label("Remove All");
  entry = gtk_entry_new();

  gtk_box_pack_start(GTK_BOX(hbox), add, FALSE, TRUE, 3);
  gtk_box_pack_start(GTK_BOX(hbox), entry, FALSE, TRUE, 3);
  gtk_box_pack_start(GTK_BOX(hbox), remove, FALSE, TRUE, 3);
  gtk_box_pack_start(GTK_BOX(hbox), removeAll, FALSE, TRUE, 3);

  gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, TRUE, 3);

  gtk_container_add(GTK_CONTAINER (sw), list);
  gtk_container_add(GTK_CONTAINER (window), vbox);

  init_list(list);

  selection  = gtk_tree_view_get_selection(GTK_TREE_VIEW(list));

  g_signal_connect(G_OBJECT(add), "clicked",
          G_CALLBACK(append_item), entry);

  g_signal_connect(G_OBJECT(remove), "clicked",
          G_CALLBACK(remove_item), selection);

  g_signal_connect(G_OBJECT(removeAll), "clicked",
          G_CALLBACK(remove_all), selection);

  g_signal_connect (G_OBJECT (window), "destroy",
          G_CALLBACK(gtk_main_quit), NULL);

  gtk_widget_show_all(window);

  gtk_main ();

  return 0;
}
</pre>

<p>
与前面的例子中的label不同的是，我们生成了三个按钮和一个单行文本输入框。我们将实现能够动态的为列表增加一个新的数据项或者去处选中的数据项以及全部数据项。 
</p>

<pre class="explanation">
 sw = gtk_scrolled_window_new(NULL, NULL);
 ...

 gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW(sw),
           GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);

 gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW(sw),
           GTK_SHADOW_ETCHED_IN);

 ...
 gtk_box_pack_start(GTK_BOX(vbox), sw, TRUE, TRUE, 5);
 ...
 gtk_container_add(GTK_CONTAINER (sw), list);
</pre>

<p>
<b class="keyword">GtkTreeView</b>构件被放置在带有滑块的窗口中。 
</p> 

<pre class="explanation">
 if (gtk_tree_selection_get_selected(GTK_TREE_SELECTION(selection), 
     &amp;model, &amp;iter)) {
   gtk_list_store_remove(store, &amp;iter);
 }
</pre>

<p>
 系统函数 <b class="keyword">gtk_list_store_remove()</b>的功能是去处列表中的所选的数据项。
</p>

<pre class="explanation">
 gtk_list_store_clear(store);
</pre>

<p>
系统函数<b class="keyword">gtk_list_store_clear()</b>将用于清除列表中的所有数据项。 
</p>

<pre class="explanation">
 if (gtk_tree_model_get_iter_first(model, &amp;iter) == FALSE) 
     return;
</pre>

<p>
上面的代码是用于检查是否在列表中还存有剩下的数据项。很显然，我们能够把列表清除的一干二净。 
</p>


<br>
<img src="../images/manage.png" alt="Advanced List View">
<div class="figure">Figure: Advanced List View</div>
<br>


<h2>树状视图（Tree View）</h2>

<p>
接着，我们将向大家展示如何运用构件<b class="keyword">GtkTreeView</b>来去显示有等级差异的数据项。在先前的两个例子中，我们我们用到了列表试图，现在我们介绍树状视图。 

</p>

<pre class="code">
#include &lt;gtk/gtk.h&gt;

enum
{
  COLUMN = 0,
  NUM_COLS
} ;

void  on_changed(GtkWidget *widget, gpointer statusbar) 
{
  GtkTreeIter iter;
  GtkTreeModel *model;
  char *value;


  if (gtk_tree_selection_get_selected(
      GTK_TREE_SELECTION(widget), &amp;model, &amp;iter)) {

    gtk_tree_model_get(model, &amp;iter, COLUMN, &amp;value,  -1);
    gtk_statusbar_push(GTK_STATUSBAR(statusbar),
        gtk_statusbar_get_context_id(GTK_STATUSBAR(statusbar), 
            value), value);
    g_free(value);
  }
}


static GtkTreeModel *
create_and_fill_model (void)
{
  GtkTreeStore *treestore;
  GtkTreeIter toplevel, child;

  treestore = gtk_tree_store_new(NUM_COLS,
                  G_TYPE_STRING);

  gtk_tree_store_append(treestore, &amp;toplevel, NULL);
  gtk_tree_store_set(treestore, &amp;toplevel,
                     COLUMN, "Scripting languages",
                     -1);

  gtk_tree_store_append(treestore, &amp;child, &amp;toplevel);
  gtk_tree_store_set(treestore, &amp;child,
                     COLUMN, "Python",
                     -1);
  gtk_tree_store_append(treestore, &amp;child, &amp;toplevel);
  gtk_tree_store_set(treestore, &amp;child,
                     COLUMN, "Perl",
                     -1);
  gtk_tree_store_append(treestore, &amp;child, &amp;toplevel);
  gtk_tree_store_set(treestore, &amp;child,
                     COLUMN, "PHP",
                     -1);

  gtk_tree_store_append(treestore, &amp;toplevel, NULL);
  gtk_tree_store_set(treestore, &amp;toplevel,
                     COLUMN, "Compiled languages",
                     -1);

  gtk_tree_store_append(treestore, &amp;child, &amp;toplevel);
  gtk_tree_store_set(treestore, &amp;child,
                     COLUMN, "C",
                     -1);

  gtk_tree_store_append(treestore, &amp;child, &amp;toplevel);
  gtk_tree_store_set(treestore, &amp;child,
                     COLUMN, "C++",
                     -1);

  gtk_tree_store_append(treestore, &amp;child, &amp;toplevel);
  gtk_tree_store_set(treestore, &amp;child,
                     COLUMN, "Java",
                     -1);

  return GTK_TREE_MODEL(treestore);
}



static GtkWidget *
create_view_and_model (void)
{
  GtkTreeViewColumn *col;
  GtkCellRenderer *renderer;
  GtkWidget *view;
  GtkTreeModel *model;

  view = gtk_tree_view_new();

  col = gtk_tree_view_column_new();
  gtk_tree_view_column_set_title(col, "Programming languages");
  gtk_tree_view_append_column(GTK_TREE_VIEW(view), col);

  renderer = gtk_cell_renderer_text_new();
  gtk_tree_view_column_pack_start(col, renderer, TRUE);
  gtk_tree_view_column_add_attribute(col, renderer, 
      "text", COLUMN);

  model = create_and_fill_model();
  gtk_tree_view_set_model(GTK_TREE_VIEW(view), model);
  g_object_unref(model); 

  return view;
}


int
main (int argc, char **argv)
{
  GtkWidget *window;
  GtkWidget *view;
  GtkTreeSelection *selection; 
  GtkWidget *vbox;
  GtkWidget *statusbar;

  gtk_init(&amp;argc, &amp;argv);

  window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
  gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
  gtk_window_set_title(GTK_WINDOW(window), "Tree View");
  gtk_widget_set_size_request (window, 350, 300);


  vbox = gtk_vbox_new(FALSE, 2);
  gtk_container_add(GTK_CONTAINER(window), vbox);


  view = create_view_and_model();
  selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(view));

  gtk_box_pack_start(GTK_BOX(vbox), view, TRUE, TRUE, 1);

  statusbar = gtk_statusbar_new();
  gtk_box_pack_start(GTK_BOX(vbox), statusbar, FALSE, TRUE, 1);

  g_signal_connect(selection, "changed", 
      G_CALLBACK(on_changed), statusbar);

  g_signal_connect (G_OBJECT (window), "destroy",
          G_CALLBACK(gtk_main_quit), NULL);

  gtk_widget_show_all(window);

  gtk_main();

  return 0;
}
</pre>

<p>
在我们上面的示例中，我们来完成一项任务：把脚本语言和传统编程语言对应的数据项，进行区分。“语言种类”作为其对应数据项中的顶层节点，也就是说是一行数据列表的“头头”。当前选种的数据项，将在状态栏中显示出来。 
</p>

<p>
从上面的这些步骤中，我们可以清晰的看到，树状视图与列表视图的生成方法很相似。 
</p>

<pre class="explanation">
 GtkTreeStore *treestore;
</pre>

<p>
这里我们当然要使用一个不同的model—— <b class="keyword">GtkTreeStore</b>。
</p>


<pre class="explanation">
 treestore = gtk_tree_store_new(NUM_COLS,
                 G_TYPE_STRING);
</pre>

<p>
我们生成的 <b class="keyword">GtkTreeStore</b>只有一列。
</p>

<pre class="explanation">
 gtk_tree_store_append(treestore, &amp;toplevel, NULL);
 gtk_tree_store_set(treestore, &amp;toplevel,
                    COLUMN, "Scripting languages",
                    -1);
</pre>

<p>
这其中的代码就是在完成一个顶层节点的操作。 
</p>

<pre class="explanation">
 gtk_tree_store_append(treestore, &amp;child, &amp;toplevel);
 gtk_tree_store_set(treestore, &amp;child,
                    COLUMN, "Python",
                    -1);
</pre>

<p>
上面的代码在生成一个子数据项。 
</p>

<br>
<img src="../images/treeview.png" alt="Tree View">
<div class="figure">Figure: Tree View</div>
<br>

<div class="center"> 
<script type="text/javascript"><!--
google_ad_client = "pub-9706709751191532";
/* horizontal */
google_ad_slot = "1734478269";
google_ad_width = 468;
google_ad_height = 60;
//-->
</script> 
<script type="text/javascript"
src="http://pagead2.googlesyndication.com/pagead/show_ads.js"> 
</script> 
</div>

<div class="botNav, center">
<span class="botNavItem"><a href="/">Home</a></span> ‡ <span class="botNavItem"><a href="..">Contents</a></span> ‡ 
<span class="botNavItem"><a href="#">Top of Page</a></span>
</div>


<div class="footer">
<div class="signature">
<a href="/">ZetCode</a> last modified April 2, 2008  <span class="copyright">&copy; 2007 - 2012 Jan Bodnar</span>
<span class="copyright">Translation &copy; 2008 chinalmy</span>
</div>
</div>

</div> <!-- content -->

</div> <!-- container -->

</body>
</html>


